package org.zjx.client;


import lombok.Data;
import lombok.SneakyThrows;
import org.zjx.core.SocketConfig;
import org.zjx.message.CommandMessage;
import org.zjx.message.HeartbeatMessage;
import org.zjx.message.Message;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Data
public class SocketClient {
    private final SocketConfig config;
    private Socket socket;
    private PrintWriter out;
    private BufferedReader in;
    private ScheduledExecutorService heartbeatExecutor;
    private volatile boolean connected = false;
    private MessageListener messageListener;
    private Thread receiveThread;
    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    public SocketClient(SocketConfig config) {
        this.config = config;
    }

    @SneakyThrows
    public void connect(){
        if (!this.connected) {
            this.socket = new Socket(this.config.getServerHost(), this.config.getPort());
            this.out = new PrintWriter(this.socket.getOutputStream(), true);
            this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            this.connected = true;
            String var10001 = this.config.getServerHost();
            this.logInfo("Connected to server " + var10001 + ":" + this.config.getPort());
            this.startHeartbeat();
            this.receiveThread = new Thread(this::receiveMessages, "SocketClient-Receiver");
            this.receiveThread.setDaemon(true);
            this.receiveThread.start();
        }
    }

    public void disconnect() {
        if (this.connected) {
            this.connected = false;
            try {
                // 1. 关闭输出流（触发服务端读取结束）
                if (this.out != null) {
                    this.out.close();
                    this.out = null;
                }

                // 2. 停止心跳线程（等待当前任务完成）
                if (this.heartbeatExecutor != null) {
                    this.heartbeatExecutor.shutdown();
                    if (!this.heartbeatExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
                        this.heartbeatExecutor.shutdownNow();
                    }
                }

                // 3. 中断接收线程并等待
                if (this.receiveThread != null && this.receiveThread.isAlive()) {
                    this.receiveThread.interrupt();
                    this.receiveThread.join(5000); // 等待最多5秒
                }

                // 4. 关闭输入流（此时应已无阻塞）
                if (this.in != null) {
                    this.in.close();
                    this.in = null;
                }

                // 5. 最后关闭套接字
                if (this.socket != null) {
                    this.socket.close();
                    this.socket = null;
                }

                this.logInfo("Disconnected from server");
            } catch (Exception e) {
                this.logError("Error closing connection", e);
            }
        }
    }

    public void sendCommand(String command, Map<String, Object> params) {
        if (!this.connected) {
            this.logWarn("Not connected, command not sent");
        } else {
            CommandMessage cmd = new CommandMessage(command);
            cmd.setMessageId("CMD_" + System.currentTimeMillis());
            cmd.setParams(params);
            this.sendMessage(cmd);
        }
    }

    private void sendMessage(Message message) {
        this.out.println(message.toJsonString());
    }

    private void startHeartbeat() {
        this.heartbeatExecutor = Executors.newSingleThreadScheduledExecutor();
        this.heartbeatExecutor.scheduleAtFixedRate(() -> {
            if (this.connected) {
                try {
                    HeartbeatMessage heartbeat = new HeartbeatMessage();
                    heartbeat.setMessageId("HB_" + System.currentTimeMillis());
                    this.sendMessage(heartbeat);
                    this.logDebug("Heartbeat sent to server");
                } catch (Exception e) {
                    this.logError("Heartbeat sending failed", e);
                    this.disconnect();
                }

            }
        }, 0L, this.config.getHeartbeatInterval(), TimeUnit.MILLISECONDS);
    }

    private void receiveMessages() {
        try {
            while(!Thread.currentThread().isInterrupted() && this.connected) {
                String responseLine = this.in.readLine();
                if (responseLine == null) break; // 服务器主动关闭连接

                try {
                    Message message = Message.parse(responseLine);
                    if (this.messageListener != null) {
                        this.messageListener.onMessage(message);
                    }

                    this.logDebug("Message received: " + String.valueOf(message.getType()));
                } catch (Exception e) {
                    this.logError("Error parsing message: " + responseLine, e);
                }
            }
        } catch (IOException e) {
            if (this.connected) {
                this.logError("Connection lost", e);
                this.disconnect();
            }
        }finally {
            // 确保线程退出时释放资源
            Thread.currentThread().interrupt();
        }

    }

    private void logDebug(String message) {
    }

    private void logInfo(String message) {
        System.out.println(this.formatLog("INFO", message));
    }

    private void logWarn(String message) {
        System.err.println(this.formatLog("WARN", message));
    }

    private void logError(String message) {
        System.err.println(this.formatLog("ERROR", message));
    }

    private void logError(String message, Throwable e) {
        System.err.println(this.formatLog("ERROR", message));
        if (e != null) {
            e.printStackTrace(System.err);
        }

    }

    private String formatLog(String level, String message) {
        return String.format("[%s] [%s] %s", this.dateFormat.format(new Date()), level, message);
    }

    public interface MessageListener {
        void onMessage(Message var1);
    }
}
